package md.cm.unit;

import graphql.schema.DataFetchingEnvironment;
import graphql.schema.GraphQLObjectType;
import lombok.*;
import md.cm.geography.Adminunit;
import md.specialEqp.Eqp;
import org.fjsei.yewu.filter.Uunode;
import org.fjsei.yewu.util.Tool;
import org.hibernate.Hibernate;
import org.hibernate.annotations.CacheConcurrencyStrategy;
import org.hibernate.search.engine.backend.types.Aggregable;
import org.hibernate.search.mapper.pojo.mapping.definition.annotation.KeywordField;

import jakarta.persistence.*;
import jakarta.validation.constraints.NotNull;
import java.util.Objects;
import java.util.Set;
import java.util.UUID;

/**单位底下细分出来的分支机构branch office｛安全管理部门或分支机构｝；　　实际是关注到了某个企业的内部组织架构；
 因为监察要求把证书，或资格，或其它管理约束规定，最小划分单元进一步细化，细化到了企业部门级别。
 维保单位管理关注到下设置 有细分的维保部门; 驻点挂接/解除维保 驻点设备
 使用单位管理关注到下设置 有细分的分支机构|内部设立管理部门；
* 特检院组织架构的部门{分院、大部门、非独立法人的大事业部}。
 * Division部门{=department} 必定属于某一个Unit 公司;
 * 使用单位的分支机构和部门的自身数据应该由外部平台为维护者，本平台只是同步复制外部数据源中的分支机构部门数据。
 * 应该避免多头共同修改维护一个数据实体：Division较为特殊！使用单位的分支机构部门最好交由监察提供给本平台，监察负责修改，我方同步外部数据源。
 * 分支机构(分公司)有自己的统一社会信用代码/纳税人识别号：就是不同于总公司的代码? 那么这个分支机构的含义明显不是我这里部门或分支机构的含义。
 *加上 @ToString 会导致死循环 ？ @ToString不能随意加
*/
@AllArgsConstructor
@NoArgsConstructor
@Entity
@Table( uniqueConstraints = {@UniqueConstraint(columnNames={"unit_id", "name"})} )
@Getter
@Setter
@Builder(toBuilder=true)
@org.hibernate.annotations.Cache(usage = CacheConcurrencyStrategy.TRANSACTIONAL, region ="Slow")
public class Division implements Uunode {
    //有旧数据的情形id可能从最大id重新开始编号
    @KeywordField(aggregable=Aggregable.YES)
    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    private UUID id;
    //用上@UniqueConstraint(columnNames={"name", "unit"}) 报错了 database column 'unit' not found. "unit_id"注意字段顺序！

    //关键字之一：[unit+ division.name]是关键字
    private String name;    //部门名字　；不是【完整合成名字】 单位名||'-'||分支机构部门名 -|| ?‘科室名’

    //到底是直属部门，还是更加独立的机构/分公司。   设备.管理部门类型mtp=2 => branch=true;
    /**此使用单位的分支机构并不在我这个监察机构管辖的区域范围内！？对于监察平台才有意义的。
     * 分支机构有独立地址的，跨区域？， 管理部门却 无独立地址的？。
     * */
    Boolean  branch;    // mtp=1 (管理)部门 ;   mtp=2 安全分支机构=true；
    //监察针对分支机构有 "CERT_ID", COMMENTS: "许可证ID", DATA_TYPE: "NUMBER" 还有类比单位的"重点单位设立原因"字段。

    //Eqp表当中的设备联系人 使用单位联系人 可能替换该字段。　原来每一个设备都有自己的联系人，新版联系人从设备表移挂到单位机构表下。
    private String linkMen;     //TB_EQP_MGE.USE_LKMEN  '操作人员/联系人'
    private String phone;       //可发送短信的手机號碼；TB_EQP_MGE.USE_PHONE  '联系人电话'
    private String tel;     //更多聯係方式，可以上多個電話號碼。
    //分支机构才需要设置正式的地址字段。
    //安全管理部门不能设置正式的地址字段/可有内部说话的办公地点。
    //对外声称正式地址；
    /**不要area也就是lare所对应的区划地址前缀；
     * 只需要后面的描述。录入时刻应该舍弃区划地址前缀。
     * */
    private String address;     //UNT_ADDR
    //单向关联，　对方可不必知道我这一方的存在。
   // @ManyToOne(fetch= FetchType.LAZY)
   // @JoinColumn
   // private Address pos;    //多对1，多端来存储定义实体ID字段。 ；地理定位。
    /**扩展地址文本串[门牌号] 更能精确定位快递地址
     * pos受限于确认地址的管理和ES搜索存储资源限制，数量相对有限，仅依靠Adrress pos所能表达的空间有限。extadr配套地址更加精确的叙述。
     * */
   // private String  extadr;

    //过渡用　地区代码
    @Deprecated
    private String  area;   //UntSecudept UntDept . 　_AREA_COD
    //MGE_DEPT_TYPE=1-分支机构.SECUDEPT_ID 关联TB_UNT_SECUDEPT; if=2-安全管理部门.SAFE_DEPT_ID  关联TB_UNT_DEPT

    /** 配合address给出合成的 邮递地址描述。 和lare不一样的； 快递使用的ad需要尽量定位最小行政单元。
     * */
    @ManyToOne(fetch= FetchType.LAZY)
    @JoinColumn
    @ToString.Exclude
    private Adminunit ad;

    //检验部门应该是Task.底下的字段：前端或后端配合决定建议给某个部门做的业务，根据Adrress行政区划?+技术参数+设备级别...每个检验机构不一样规则。
    /**部门管辖地域，行政区划 或级别： 和ad不一样；
     * 针对每个检验机构的检验部门的自动选择：【检验部门】(Task.dep?)Eqp实体没有独立的字段？应该归属哪一个部门的业务?/行政区划划分?业务类别不一样有可能也不同,?动态优先选择概念
     * Unit->Division->Office准备了3个级别一般地企业[本平台涉及]组织架构模型。 监察机构还不止3个级别啊{行政级别可独立法人可以独立出来实体单位Unit,【地级市->区县->[所级]街道市场监管人员】}。
     * 监察机构如果直接按照地市级别注册一个Unit也可行的，没必要每一个区县都独立配置一个Unit{type.Has[]=特种设备监察机构}。
     * */
    @ManyToOne(fetch= FetchType.LAZY)
    @JoinColumn
    @ToString.Exclude
    private Adminunit lare;

    /**使用单位用到了
    分支机构， 任务生成区分不同的分支(根本就不同的科室负责的作业对象吧)？没啥可再去分。
    出发点？监管细分照顾的？还是检验业务收钱对象差异，或是报告证书发送对象差异。
    单位分解规则，机构识别代码 统一社会信用代码 UNT_ORG_COD。大公司可以分开多个信用代码=多个单位。
    */
    @OneToMany(mappedBy = "usud")
    @ToString.Exclude
    private Set<Eqp> uses;    //使用单位 在用设备集合

    /**维保单位用到了, 交给监察的系统 去搞
    */
   // @OneToMany(mappedBy = "mtud")
   // private Set<Eqp> maints;    //维保单位 维保设备集合


    /*
    报错 hashCode().StackOverflowError  解决方法：使用@Setter和@Getter代替@Data;
    或者用 @ToString.Exclude   @EqualsAndHashCode.Exclude来屏蔽。
    * */
    //本分支机构到底归属于哪一个单位的？　管理意义上的细化：　单位Unit 1-->N 分支。 (fetch= FetchType.LAZY)
    @NotNull
    @ManyToOne(fetch= FetchType.EAGER)
    @JoinColumn
    private Unit  unit;
    //目前，假设UntSecudept 和UntDept 两个表的ID不重复！[监察也有检验也有这两个表的]
   //private Long  oldId;   //对接旧检验平台 内设管理部门 .SAFE_DEPT_ID  关联TB_UNT_DEPT ,内设分支机构 .SECUDEPT_ID 关联TB_UNT_SECUDEPT
    //一个单位 底线的 分支机构和管理部门的名字还可能重复吗？[unit+ division.name]是关键字！

    /*来自哪一个旧表。 ？可是不同监察机构的数据还一样呢？以监察为主动方。想跟多个外部数据源同步困难，没有数据的主控方，仅仅汇集。
    沉淀数据无法删除！ 若外部数据源已经删除了某个分支机构部门，那么能保证本平台也能够彻底删除与同步外部数据源？更新和增加可同步。额外反向Keyword搜索校对。
    部门：只能增加更新，无法被删除！ 假设多个数据源？应该多个数据源反方向搜索匹配看看！定位到Unit+部门名称。
    部门表也是被动接受：可能多份来源都有该部门，标识最后哪一个同步来源地。
    不是使用单位的那些 本平台用户归属单位也有部门管理的，这样的单位的某些部门是本平台自己维护，受制于外部管理的部门数据维护？。
    * */
    private String  frot;     //采信数据来源！【Enum标签】 , null=本平台自己添加维护的。

    /**科室 细分。
     */
    @OneToMany(mappedBy = "dep")
    @ToString.Exclude
    private Set<Office>  offices;

    //Hibernate Search 要求添加的字段！
    @OneToMany(mappedBy = "ispud")
    private Set<Eqp>   ispEqps;


    //@Transient public String getStaff(DataFetchingEnvironment env){} 可没法操作数据库
    public String getId(DataFetchingEnvironment env) {
        return Tool.toGlobalId(this.getClass().getSimpleName(), this.id);
    }
//    public String id() {
//        return Tool.toGlobalId(this.getClass().getSimpleName(), String.valueOf(this.id));
//    }
//    public String getId(DataFetchingEnvironment env){
//        String typeName= this.getClass().getSimpleName();
//        return Tool.toGlobalId(typeName, this.getId());
//    }

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || Hibernate.getClass(this) != Hibernate.getClass(o)) return false;
        Division ent = (Division) o;
        return id != null && Objects.equals(id, ent.id);
    }
    /* 不能用 lombOk.@Data注解：死循环报错；只能手动添加hashCode()equals()代码;
    对于equals()相等的对象hash码要一定相等，而对于equals()不同的对象要尽量做到hash码不同。
    重写equals，也必须重写hashCode。  缺省的getClass().hashCode()不会变;
    重写equals和hashcode一定要注意equals的规则和hashcode的算法，否则会造成程序性能急剧下降。
    * */
    @Override
    public int hashCode() {
        return (int) (getClass().hashCode() + id.hashCode());
    }
}



//MGE_DEPT_TYPE=1安全管理部门TB_UNT_DEPT，MGE_DEPT_TYPE=2分支机构TB_UNT_SECUDEPT
/*
维保单位用     comment on column TB_EQP_MGE.MANT_UNT_ID is '维保单位ID'
comment on column TB_EQP_MGE.MANT_DEPT_ID is '维保部门ID' ! ! 检验都是空着的
【旧监察】
    没有MGE_DEPT_TYPE字段！ ；"SECUDEPT_ID 分支机构ID",    "SAFE_DEPT 安全管理部门ID",
【旧检验】
    MGE_DEPT_TYPE ； "SECUDEPT_ID",分支机构ID",  "SAFE_DEPT_ID"安全管理部门ID"
    TB_EQP_MGE.USE_UNT_ID is '使用单位ID'   检验数据库: 使用单位用  管理部门类型，0:无内设
    if MGE_DEPT_TYPE=2-    内设分支机构 .SECUDEPT_ID 关联TB_UNT_SECUDEPT;
    if=1-内设管理部门 .SAFE_DEPT_ID  关联TB_UNT_DEPT
使用单位部门的数据同步： 部门要以检验为准。检验没有数据的情形：SECUDEPT分支机构可以采用监察，但是DEPT安全管理部门就要忽视掉{监察 部门太多了随意设置}。
*/



/* @数据库修改脚本：
    上面的 Adminunit ad; 结合业务需求， 暂时可不考虑为外键FOREIGN KEY (ad_id) REFERENCES public.adminunit人工添加个索引。
* */
